<?php
/**
 * Note: 日统计
 */

namespace App\Models\Mongo\DailyLog;

use App\Models\Game\Center\AppSource;
use App\Traits\ModelTrait;
use App\Traits\MongoModelTrait;
use Jenssegers\Mongodb\Collection;

class DayStat extends Base
{
    use ModelTrait, MongoModelTrait;

    protected $table = 'dayStat';

    protected $fillable = [
        'day',
        'source',
        'pay',
        'payAlipay',
        'payWechat',
        'payUser',
        'payUser2',
        'payFirst',
        'payFirstUser',
        'payFirstOldUser24',
        'payFirstOldUser48',
        'payCntAlipayFailed',
        'payCntAlipaySuccess',
        'payCntWechatFailed',
        'payCntWechatSuccess',
        'withdraw',
        'withdrawCash',
        'withdrawJd',
        'withdrawUser',
        'withdrawFirst',
        'withdrawFirstUser',
        'withdrawChannelFee',
        'paySum',
        'withdrawSum',
        'registerUser',
        'loginUser',
        'activeUser',
        'monthLoginUser',
        'cplCost',
        'cplTaskCost',
        'cpaCost',
        'maxOnline',
        'freeUserStockBullet',
        'freeUserStockHb',
        'spreadChannel',
        'spreadType',
        'spreadUser',
        'authAllowUser',
        'cpaCostFormula',
        'thirdExchange',
        'thirdExchangeUser',
        'thirdExchangeFirst',
        'thirdExchangeFirstUser',
    ];

    protected $appends = [
        'loginOldUser',
        'loginUserInMonthScale',
        'arpu',
        'arpuFirst',
        'arpuOld',
        'arppu',
        'arppuFirst',
        'arppuOld',
        'pur',
        'purFirst',
        'purOld',
        'roi',
        'roiFirst',
        'roiMarket',
        'roiMarketFirst',
        'payChannelFee',
        'payChannelFeeAlipay',
        'payChannelFeeWechat',
        'normalProfit',
        'platformProfit',
        'operationProfit',
        'payOld',
        'payOldUser',
        'payUserScale',
        'payFirstUserScale',
        'payCost',
        'payFirstCost',
        'withdrawOld',
        'withdrawOldUser',
        'withdrawUserScale',
        'withdrawFirstUserScale',
        'userProfitFirst',
        'userProfitOld',
        'registerCost',
        'spreadCost',
        'payCntAlipayScale',
        'payCntWechatScale',
        'payFirstOldUser24Cost',
        'payFirstOldUser48Cost',
        'withdrawPayScale',
        'registerScale',
        'payFirstUserWithdrawFirstUserScale',
        'thirdExchangeOld',
        'thirdExchangeOldUser',
        'thirdExchangePayScale',
        'thirdExchangeUserScale',
        'thirdExchangeFirstUserScale',
        'payFirstUserThirdExchangeFirstUserScale',
    ];

    protected $casts = [
        'pay' => 'integer',
        'payAlipay' => 'integer',
        'payWechat' => 'integer',
        'payUser' => 'integer',
        'payFirst' => 'integer',
        'payFirstUser' => 'integer',
        'payCntAlipayFailed' => 'integer',
        'payCntAlipaySuccess' => 'integer',
        'payCntWechatFailed' => 'integer',
        'payCntWechatSuccess' => 'integer',
        'payFirstOldUser24' => 'integer',
        'payFirstOldUser48' => 'integer',
        'withdraw' => 'integer',
        'withdrawCash' => 'integer',
        'withdrawJd' => 'integer',
        'withdrawUser' => 'integer',
        'withdrawFirst' => 'integer',
        'withdrawFirstUser' => 'integer',
        'paySum' => 'integer',
        'withdrawSum' => 'integer',
        'registerUser' => 'integer',
        'loginUser' => 'integer',
        'activeUser' => 'integer',
        'monthLoginUser' => 'integer',
        'cplCost' => 'integer',
        'cplTaskCost' => 'integer',
        'cpaCost' => 'integer',
        'maxOnline' => 'integer',
        'freeUserStockBullet' => 'integer',
        'freeUserStockHb' => 'integer',
        'authAllowUser' => 'integer',
        'thirdExchange' => 'integer',
        'thirdExchangeUser' => 'integer',
        'thirdExchangeFirst' => 'integer',
        'thirdExchangeFirstUser' => 'integer',
    ];


    public const CALC_WITHOUT_FIELDS = ['day', 'source', 'spreadChannel', 'spreadType', 'spreadUser', 'cpaCostFormula'];
    /**
     * 去新增活跃人数
     * @return float|int
     */
    public function getLoginOldUserAttribute()
    {
        return $this->loginUser - $this->registerUser;
    }

    /**
     * 活跃用户/当月活跃用户占比
     * @return float|int
     */
    public function getLoginUserInMonthScaleAttribute()
    {
        return $this->monthLoginUser ? $this->loginUser/$this->monthLoginUser : 0;
    }

    /**
     * 活跃人均充值
     * @return float|int
     */
    public function getArpuAttribute()
    {
        return $this->loginUser ? $this->pay / $this->loginUser : 0;
    }

    /**
     * 活跃人均充值 新增
     * @return float|int
     */
    public function getArpuFirstAttribute()
    {
        return $this->registerUser ? $this->payFirst / $this->registerUser : 0;
    }

    /**
     * 活跃人均充值 去新增
     * @return float|int
     */
    public function getArpuOldAttribute()
    {
        if (!$this->loginOldUser) return 0;
        return $this->payOld / $this->loginOldUser;
    }

    /**
     * 付费人均充值
     * @return float|int
     */
    public function getArppuAttribute()
    {
        return $this->payUser ? $this->pay / $this->payUser : 0;
    }

    /**
     * 付费人均充值 新增
     * @return float|int
     */
    public function getArppuFirstAttribute()
    {
        return $this->payFirstUser ? $this->payFirst / $this->payFirstUser : 0;
    }

    /**
     * 付费人均充值 去新增
     * @return float|int
     */
    public function getArppuOldAttribute()
    {
        if (!$this->payOldUser) return 0;
        return $this->payOld / $this->payOldUser;
    }

    /**
     * 付费用户比例
     * @return float
     */
    public function getPurAttribute(): float
    {
        if (!$this->loginUser) return 0;
        return $this->payUser / $this->loginUser;
    }

    /**
     * 新增付费用户比例
     * @return float
     */
    public function getPurFirstAttribute(): float
    {
        if (!$this->registerUser) return 0;
        return $this->payFirstUser / $this->registerUser;
    }

    /**
     * 去新增付费用户比例
     * @return float
     */
    public function getPurOldAttribute(): float
    {
        $oldPayUser = $this->payUser - $this->payFirstUser;
        $oldLoginUser = $this->loginUser - $this->registerUser;
        if (!$oldLoginUser) return 0;
        return $oldPayUser / $oldLoginUser;
    }

    /**
     * 投资回报率
     * @return float|int
     */
    public function getRoiAttribute()
    {
        return $this->spreadCost ? (($this->pay - $this->withdraw - $this->thirdExchange/getCurrencyHbScale(true)) / $this->spreadCost) : 0;
    }

    /**
     * 首日投资回报率
     */
    public function getRoiFirstAttribute()
    {
        return $this->spreadCost ? (($this->payFirst - $this->withdrawFirst - $this->thirdExchangeFirst/getCurrencyHbScale(true)) / $this->spreadCost) : 0;
    }

    /**
     * 市场ROI
     */
    public function getRoiMarketAttribute()
    {
        return $this->spreadCost ? $this->pay / $this->spreadCost : 0;
    }

    /**
     * 首日市场ROI
     */
    public function getRoiMarketFirstAttribute()
    {
        return $this->spreadCost ? $this->payFirst / $this->spreadCost : 0;
    }

    /**
     * 支付通道费用(仅包含支付宝&微信)
     * @return float
     */
    public function getPayChannelFeeAttribute(): float
    {
        return $this->payChannelFeeAlipay + $this->payChannelFeeWechat;
    }

    /**
     * 支付通道费用(支付宝)
     * @return float
     */
    public function getPayChannelFeeAlipayAttribute(): float
    {
        return $this->payAlipay * config('game.pay_channel_fee_alipay', 0.01);
    }

    /**
     * 支付通道费用(微信)
     * @return float
     */
    public function getPayChannelFeeWechatAttribute(): float
    {
        return $this->payWechat * config('game.pay_channel_fee_wechat', 0.01);
    }

    /**
     * 净利润
     * @return float
     */
    public function getNormalProfitAttribute(): float
    {
        return $this->pay - $this->withdraw - $this->spreadCost - $this->thirdExchange/getCurrencyHbScale(true);
    }

    /**
     * 平台毛利
     * @return float
     */
    public function getPlatformProfitAttribute(): float
    {
        return $this->pay - $this->withdraw - $this->thirdExchange/getCurrencyHbScale(true);
    }

    /**
     * 运营毛利
     * @return float
     */
    public function getOperationProfitAttribute(): float
    {
        return $this->pay - $this->payChannelFee - $this->withdraw - $this->withdrawChannelFee - $this->spreadCost - $this->thirdExchange/getCurrencyHbScale(true);
    }

    /**
     * 去新增充值金额
     * @return float
     */
    public function getPayOldAttribute(): float
    {
        return $this->pay - $this->payFirst;
    }

    /**
     * 去新增充值人数
     * @return int
     */
    public function getPayOldUserAttribute(): int
    {
        return $this->payUser - $this->payFirstUser;
    }

    /**
     * 充值率 (充值人数/活跃人数)
     * @return float|int
     */
    public function getPayUserScaleAttribute(): float
    {
        return $this->loginUser ? $this->payUser/$this->loginUser : 0;
    }

    /**
     * 首日充值率 (首日充值人数/注册人数)
     * @return float|int
     */
    public function getPayFirstUserScaleAttribute(): float
    {
        return $this->registerUser ? $this->payFirstUser/$this->registerUser : 0;
    }

    /**
     * 付费成本
     * @return float|int
     */
    public function getPayCostAttribute(): float
    {
        return $this->payUser ? $this->spreadCost / $this->payUser : 0;
    }

    /**
     * 付费成本
     * @return float|int
     */
    public function getPayFirstCostAttribute(): float
    {
        return $this->payFirstUser ? $this->spreadCost / $this->payFirstUser : 0;
    }

    /**
     * 去新增权益金额
     * @return float
     */
    public function getWithdrawOldAttribute(): float
    {
        return $this->withdraw - $this->withdrawFirst;
    }

    /**
     * 去新增积分数量
     * @return float
     */
    public function getThirdExchangeOldAttribute(): float
    {
        return $this->thirdExchange - $this->thirdExchangeFirst;
    }

    /**
     * 去新增权益人数
     * @return int
     */
    public function getWithdrawOldUserAttribute(): int
    {
        return $this->withdrawUser - $this->withdrawFirstUser;
    }

    /**
     * 去新增积分人数
     * @return int
     */
    public function getThirdExchangeOldUserAttribute(): int
    {
        return $this->thirdExchangeUser - $this->thirdExchangeFirstUser;
    }

    /**
     * 权益率 (权益人数/活跃人数)
     * @return float
     */
    public function getWithdrawUserScaleAttribute(): float
    {
        return $this->loginUser ? $this->withdrawUser/$this->loginUser : 0;
    }

    /**
     * 积分率 (积分人数/活跃人数)
     * @return float
     */
    public function getThirdExchangeUserScaleAttribute(): float
    {
        return $this->loginUser ? $this->thirdExchangeUser/$this->loginUser : 0;
    }

    /**
     * 首日权益率 (首日权益人数/当日注册数)
     * @return float|int
     */
    public function getWithdrawFirstUserScaleAttribute(): float
    {
        return $this->registerUser ? $this->withdrawFirstUser/$this->registerUser : 0;
    }

    /**
     * 首日积分率 (首日积分人数/当日注册数)
     * @return float|int
     */
    public function getThirdExchangeFirstUserScaleAttribute(): float
    {
        return $this->registerUser ? $this->thirdExchangeFirstUser/$this->registerUser : 0;
    }

    /**
     * 新增用户毛利
     * @return int
     */
    public function getUserProfitFirstAttribute(): int
    {
        return $this->payFirst - $this->thirdExchangeFirst/getCurrencyHbScale(true);
    }

    /**
     * 去新增用户毛利
     * @return int
     */
    public function getUserProfitOldAttribute(): int
    {
        return $this->pay - $this->payFirst - (($this->thirdExchange - $this->thirdExchangeFirst)/getCurrencyHbScale(true));
    }

    /**
     * 注册成本 推广费/注册人数
     * @return float|int
     */
    public function getRegisterCostAttribute()
    {
        return $this->registerUser ? $this->spreadCost / $this->registerUser : 0;
    }

    /**
     * 推广成本
     * @return mixed
     */
    public function getSpreadCostAttribute(): int
    {
        return $this->cplCost + $this->cpaCost;
    }

    /**
     * 支付次数的比例 failed/success
     * @return float
     */
    public function getPayCntAlipayScaleAttribute(): float
    {
        $total = $this->payCntAlipayFailed + $this->payCntAlipaySuccess;
        return $total ? $this->payCntAlipayFailed / $total : 0;
    }

    /**
     * 支付次数的比例 failed/success
     * @return float
     */
    public function getPayCntWechatScaleAttribute(): float
    {
        $total = $this->payCntWechatFailed + $this->payCntWechatSuccess;
        return $total ? $this->payCntWechatFailed / $total : 0;
    }

    /**
     * 24小时内首次充值成本
     * @return float
     */
    public function getPayFirstOldUser24CostAttribute(): float
    {
        return $this->payFirstOldUser24 ? $this->spreadCost/$this->payFirstOldUser24 : 0;
    }

    /**
     * 48小时内首次充值成本
     * @return float
     */
    public function getPayFirstOldUser48CostAttribute(): float
    {
        return $this->payFirstOldUser48 ? $this->spreadCost/$this->payFirstOldUser48 : 0;
    }

    /**
     * 兑换/充值
     * @return float|int
     */
    public function getWithdrawPayScaleAttribute()
    {
        return $this->pay ? $this->withdraw/$this->pay : 0;
    }

    /**
     * 积分/充值 比例
     * @return float|int
     */
    public function getThirdExchangePayScaleAttribute()
    {
        return $this->pay ? $this->thirdExchange/getCurrencyHbScale(true)/$this->pay : 0;
    }

    /**
     * 注册率
     * @return float|int
     */
    public function getRegisterScaleAttribute()
    {
        return $this->authAllowUser ? $this->registerUser/$this->authAllowUser : 0;
    }

    /**
     * 首日转化率
     * 首日充值人数/首日权益人数
     * @return float|int
     */
    public function getPayFirstUserWithdrawFirstUserScaleAttribute()
    {
        return $this->withdrawFirstUser ? $this->payFirstUser/$this->withdrawFirstUser : 0;
    }

    /**
     * 首日积分转化率
     * 首日充值人数/首日积分人数
     * @return float|int
     */
    public function getPayFirstUserThirdExchangeFirstUserScaleAttribute()
    {
        return $this->thirdExchangeFirstUser ? $this->payFirstUser/$this->thirdExchangeFirstUser : 0;
    }

    /**
     * 关联 渠道
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo|\Jenssegers\Mongodb\Relations\BelongsTo
     */
    public function appSource()
    {
        return $this->belongsTo(AppSource::class, 'source', 'id');
    }

    /**
     * 按天统计
     * @param string $day
     * @param bool $append
     * @return array
     */
    public function getSumDataByDay(string $day, bool $append = false): array
    {
        $fields = array_filter($this->fillable, function($field) {
            return !in_array($field, self::CALC_WITHOUT_FIELDS);
        });
        $groups = ['_id' => '$day'];
        foreach ($fields as $field) {
            $groups[$field] = [
                '$sum' => '$'.$field,
            ];
        }
        $aggregate = [
            [
                '$match' => [
                    'day' => $day,
                    'source' => [
                        '$ne' => 0,
                    ]
                ],
            ],
            [
                '$group' => $groups,
            ],
        ];
        $res = DayStat::raw(function (Collection $collection) use ($aggregate) {
            return $collection->aggregate($aggregate);
        });

        if (!$res = $res->first()) {
            return [];
        }
        $res = $res->setHidden(['_id']);
        if (!$append) $res = $res->setAppends([]);
        return $res->toArray();
    }

    /**
     * 按渠道统计
     * @param int $source
     * @param array $dateRange
     * @param bool $append
     * @return array
     */
    public function getSumDataBySource(int $source, array $dateRange = [], bool $append = false): array
    {
        $fields = array_filter($this->fillable, function($field) {
            return !in_array($field, self::CALC_WITHOUT_FIELDS);
        });
        $groups = ['_id' => '$source'];
        foreach ($fields as $field) {
            $groups[$field] = [
                '$sum' => '$'.$field,
            ];
        }
        $aggregate = [
            [
                '$match' => [
                    'day' => [
                        '$gte' => $dateRange[0],
                        '$lte' => $dateRange[1],
                    ],
                    'source' => $source,
                ],
            ],
            [
                '$group' => $groups,
            ],
        ];
        $res = self::raw(function (Collection $collection) use ($aggregate) {
            return $collection->aggregate($aggregate);
        });
        if (!$res = $res->first()) {
            return [];
        }
        $res = $res->setHidden(['_id']);
        if (!$append) $res = $res->setAppends([]);
        return $res->toArray();
    }

    /**
     * 根据渠道获取数据
     * @param array $source
     * @param array $spreadUser
     * @param array $dateRange
     * @param null $groups
     * @return array
     */
    public function getDataBySource(array $source = [], array $spreadUser = [], array $dateRange = [], $groups = null): array
    {
        $fields = array_filter($this->fillable, function($field) {
            return !in_array($field, self::CALC_WITHOUT_FIELDS);
        });

        if (!$groups) {
            $groups = [
                '_id' => '$day',
                'day' => [
                    '$last' => '$day'
                ],
            ];
        }
        foreach ($fields as $field) {
            $groups[$field] = [
                '$sum' => '$'.$field,
            ];
        }
        $match = [
            'day' => [
                '$gte' => $dateRange[0],
                '$lte' => $dateRange[1],
            ],
            'source' => [
                '$ne' => 0,
            ],
        ];

        if ($source) {
            $match['source'] = [
                '$in' => array_map('intval', $source),
            ];
        }
        if ($spreadUser) {
            $match['spreadUser'] = [
                '$in' => array_map('trim', $spreadUser),
            ];
        }

        $aggregate = [
            [
                '$match' => $match,
            ],
            [
                '$group' => $groups,
            ],
            [
                '$sort' => [
                    '_id' => 1,
                ],
            ],
        ];
        $res = self::raw(function (Collection $collection) use ($aggregate) {
            return $collection->aggregate($aggregate);
        });
        if (!$res->count()) return [];

        return $res->toArray();
    }
}
